var g_tagOrders = {};
var g_colorNames = ['none', 'yellow', 'red', 'green', 'blue', 'cyan', 'gray'];
var g_colorLabels = g_colorNames;
var g_hosted = host.isHosted();

var interop;
var undefined;

var g_tagNames = ['important', 'critical', '-', 'star', 'idea', 'question',
    '-', 'email', 'web', 'download', '-', 'phone', 'book', 'pill', 'shopping'];
var g_tagLabels = g_tagNames;

var $table;
var divFullWidth;

$(function () {
    var n;

    $table = $('#tblTodo');
    divFullWidth = document.getElementById('divFullWidth');

    /* printing */
    //*
    window.onbeforeprint = window_beforePrint;//*/
    window.onafterprint = window_afterPrint;


    //
    localize($table);



    var $tbody = $table.children('tbody.nodes');
    $tbody.children('script').remove();

//    $(document).on('keydown', function(e) {
//       console.log('@@@@@@@@@ document_keydown: ' + e.which);
//    });
    $table.on('keydown', undecorated.interceptKeydown);

    if (!host.isProgressVisible()) {
        $table.addClass('hideProgress');
        $table.children('thead').find('th[data-progress]').addClass('hidden');
    }
    if (!host.isDueVisible()) {
        $table.addClass('hideDue');
        $table.children('thead').find('th[data-due]').addClass('hidden');
    }
    if (!host.isDescVisible()) {
        $table.addClass('hideDesc');
        $table.children('thead').find('th[data-desc]').addClass('hidden');
    }

    $table.toggleClass('hideChecked', !(host.isDoneItemsVisible()));

    //*
    $table.colResizer({
//        debug: true,
        fixedTableWidth: false,
        xx: 1
    });
    //*/

    todolist.setTable($table);

    var editor;
    var CustomInputEditor = undecorated.Editable.CustomInputEditor;
    $table.editable({
        createEditor: function ($tb) {
            if ($tb.is('[data-due]')) {
                var $txtDue = $('#txtDue');
                editor = new CustomInputEditor($tb, $txtDue);
                return editor;
            }
            else if ($tb.is('[data-progress]')) {
                var $txtProgress = $('#txtProgress');
                var $progressBarEl = $tb.children('.progressBar');
                if (!$progressBarEl.progressBar('get')) { // lazy init
                    $progressBarEl.progressBar();
                }
                var progress = $progressBarEl.progressBar('value');
                editor = new CustomInputEditor($tb, $txtProgress, {
                    getEditText: function () {
                        return '' + progress;
                    },
                    updateTextBlock: function (text) {
                        $progressBarEl.progressBar('value', text);
                    },
                    selectAll: true
                });
                return editor;
            }
        }
    });

    var firstTr = domex.getFirstChildElelement($table.children('tbody.nodes')[0]);
    $table.on('edited', function (e, oriText, newText) {
        var $textblock = $(e.target);
        var $tr = $textblock.parents('tr:first');
        if ($textblock.hasClass('multiline')) {
            $tr.addClass('multiline');
        }
        else {
            $tr.removeClass('multiline');
        }
        undoman.onAction({
            action: 'edit',
            editable: e.target,
            oriText: oriText,
            newText: newText
        });
        host.NotifyChange();
    });

    //
    var treeAction, oriSelectedNodes, oriActiveNode;
    $table.on('treeChanging', function (e, nodes) {
        oriActiveNode = todolist.getActiveRow();
        oriSelectedNodes = todolist.getSelectedRows();
        treeAction = undoman.newAction({
            action: 'tree',
            nodes: nodes
        });
        treeAction.beforeAction();
    });
    $table.on('treeChanged', function () {

        //
        $table.treeTable('updateIndentLinesExtended', treeAction.nodes);

        //
        todolist.select(oriSelectedNodes);
        todolist.setActiveRow(oriActiveNode);
        treeAction.afterAction();
        undoman.onAction(treeAction);
        host.NotifyChange();
    });

    //
    $table.on('checkChanged', function (e, chk) {
        var $chk = $(chk);
        var newChecked = $chk.is(':checked');

        var nodes = todolist.getSelectedRows();

        undoman.beginCompoundAction();
        //
        var deactiveOnCheck = true;
        if (newChecked && deactiveOnCheck) {
            for (var n = 0; n < nodes.length; n++) {
                var $node = $(nodes[n]);
                if ($node.hasClass('active-todo')) {
                    toggleNodeClass('active-todo', $table, $node);
                    break;
                }
            }
        }

        //
        $chks = $(nodes).find(':checkbox');
        var oriChecked = [];
        $chks.each(function (n, it) {
            oriChecked.push(it === chk ? !newChecked : $(it).is(':checked'));
        });
        $table.checkableTable('setChecked', $chks, newChecked);

        undoman.onAction({
            action: 'check',
            $chks: $chks,
            oriChecked: oriChecked,
            newChecked: newChecked
        });
        undoman.endCompoundAction();
        host.NotifyChange();
    });
    $table.on('nodeChanged', function () {
        host.NotifyChange();
    });
    $table.on('rowsChanged', function () {
//        $table.treeTable('updateAltRows');
        todolist.updateAltRows();
        $table.children('.empty-indicator').toggle(todolist.isEmpty());
    });
    $table.on('rowsVisibilityChanged', function () {
        fitTableWidth();
    });
    //
    $table.on('nodeAdded nodeDeleted', function (e, node) {
        $(node).find('.progressBar').progressBar();
        fitTableWidth();
//        $table.treeTable('initNode', node);
    });

    //
    $table.on('setTextRequest', function (e, editable, text) {
        var $editable = $(editable);
        if ($editable.is('[data-progress]')) {
            var $progressBarEl = $editable.children('.progressBar');
            $progressBarEl.progressBar('value', parseInt(text));
            return true;
        }
    });

    //
    setupOverflowTooltips($table);

    //

    var $tagMenu = $('#tagMenu');
    var order = 0;
    var tagIndex = 0;
    for (n = 0; n < g_tagNames.length; n++) {
        var tagName = g_tagNames[n];
        if (tagName == '-') {
            $tagMenu.append($('<li>-</li>'));
            continue;
        }
        var tagLabel = g_tagLabels[tagIndex++];
        if (tagLabel == '-') {
            tagLabel = g_tagLabels[tagIndex++];
        }

        g_tagOrders[tagName] = ++order;

        $tagMenu.append($('<li data-tag="' + tagName + '">' +
            '<img src="image/tag/' + tagName + '.png" class="icon"/>' +
            tagLabel.substr(0, 1).toUpperCase() + tagLabel.substr(1) +
            '</li>'))
    }

    //
    var $colorMenu = $('#colorMenu');
    for (n = 0; n < g_colorNames.length; n++) {
        var colorName = g_colorNames[n];
        var colorLabel = g_colorLabels[n];
        $colorMenu.append($('<li data-color="' + colorName + '" class="radio todo-' + colorName + '">' +
            colorLabel.substr(0, 1).toUpperCase() + colorLabel.substr(1) +
            '</li>'));
        if (n == 0) {
            $colorMenu.append('<li>-</li>');
        }
    }

    //
    var $priorityMenu = $('#priorityMenu');
    for (n = 0; n <= 5; n++) {
        $priorityMenu.append('<li data-priority="' + n + '" class="radio">' + (n ? n : T('None')) + '</li>')
        if (n == 0) {
            $priorityMenu.append('<li>-</li>');
        }
    }

    //
    $priorityMenu.on('action', function (e, $li) {
        var tr = todolist.getActiveRow();
        var $tr = $(tr);
        var priority = $li.data('priority');

        updateNodeData($table, $tr, 'priority', priority);
    });

    //
    $colorMenu.on('action', function (e, $li) {
        var newColor = $li.data('color');
        var tr = todolist.getActiveRow();
        var $tr = $(tr);
        updateNodeData($table, $tr, 'color', newColor);
    });

    //
    var $ulMenu1 = $('#ulMenu1');
    $ulMenu1.popupMenu();
    var popupMenu = $ulMenu1.popupMenu('get');
    popupMenu.registerHandler({
        setupReminder: function (e, $li) {
            var $node = $(todolist.getActiveRow());
            //host.setupReminder();
            setupReminder($node);
        }
    });
    popupMenu.registerHandler(function () {
        gotFocus();
    });

    //
    var $mnActive = $('#mnActive');
    $mnActive.on('action', function () {
        var $node = $(todolist.getActiveRow());
        toggleNodeClass('active-todo', $table, $node);
    });

    $table.on('contextmenu', (host.isHosted() ? 'tr.node' : 'span.label'), function (e) {
        e.preventDefault();
        var $tr = e.target.nodeName == 'TR' ? $(tr) : $(e.target).parents('tr:first');

        var byKeyboard = false;
        if (pressingContextMenuKey) {
            pressingContextMenuKey = false;
            byKeyboard = true;
        }
        var x = e.pageX;
        var y = e.pageY;
        if (byKeyboard) {
            var $label = $tr.find('span.label');
            var offset = $label.offset();
            x = offset.left + $label.outerWidth() + 1;
            y = offset.top;
        }

        if (host.showContextMenu(x, y - $(window).scrollTop())) {
            return;
        }

        var selectedTrs = todolist.getSelectedRows();
        var tr = $tr[0];
        if ($.inArray(tr, selectedTrs) == -1) {
            //$table.selectableTable('setActiveRow', tr);
            todolist.setActiveRow(tr);
        }

        //update menu item states here
        var tags = ($tr.data('tags') || '') + ' ';
        $tagMenu.children('li').each(function (n, li) {
            var $li = $(li);
            if ($li.is('.separator')) {
                return;
            }
            var tag = $li.data('tag');
            popupMenu.setChecked(li, tags.indexOf(tag + ' ') != -1);
        });

        //update selected color here
        var selectedColor = ($tr.data('color') || 'none');
        $colorMenu.children('li').each(function (n, li) {
            var $li = $(li);
            var color = $li.data('color');
            popupMenu.setChecked(li, color == selectedColor);
        });

        //
        var selectedPriority = $tr.data('priority') || 0;
        $priorityMenu.children('li').each(function (n, li) {
            var $li = $(li);
            var priority = $li.data('priority');
            popupMenu.setChecked(li, priority == selectedPriority);
        });

        //
        var multipleSelected = todolist.getSelectedRows().length > 1;
        $('#mnSetupReminder').trigger('setDisabledRequest', [multipleSelected]);

        //
        popupMenu.setChecked($mnActive, $tr.hasClass('active-todo'));

        //

        popupMenu.showAt(x, y);
    });

    $tagMenu.on('action', function (e, $li) {
        var tag = $li.data('tag');

        toggleTag($table, tag);
    });

    //
    buildTableDecorations($table);

    //
    undoman.setTable($table);

    //
    clip.setTable($table);

    //
    $table.on('keydown', onTblTodoKeydown);

    $(document).on('mouseup', function (e) {
        if (e.target.tagName == 'HTML') {
            gotFocus();
        }
    });

    //
    $table.on('updateNodeDecorationsRequest', function (e) {
        var $node = $(e.target);
        updateNodeDecorations($node);
    });

    //
    $table.on('focus', function (e) {
        gotFocus();
    });

    //
    $(document).on('focusin', function () {
        host.NotifyUIUpdate();
    });

    //
    vimcmd.init($table);

    //
    $table.dnd();
    $tbody.boxSelector();

    $('#divToast').toast();

    //
    initTodoApi();

    //
    if (!host.isGridLineVisible()) {
        $table.addClass('hideGridLine');
    }
    if (!host.isTreeLineVisible()) {
        $table.addClass('hideTreeLine');
    }
    if (!host.isHeaderVisible()) {
        $table.addClass('hideHeader').trigger('headerChange');
    }


    //
    loadColWidths();
    setTheme(host.getTheme());


    //
    host.NotifyDocReady();
});

/*--------------------------------------------------------------*/

function toggleTag($table, tag) {
    var tr = todolist.getActiveRow();
    var $tr = $(tr);
    var addTag = !hasTag($tr, tag);

    var nodes = todolist.getSelectedRows();

    var oriData = [];
    var newData = [];
    for (var n = 0; n < nodes.length; n++) {
        var $node = $(nodes[n]);
        var oriTags = $node.data('tags');
        oriData.push(oriTags);

        var tags = ($node.data('tags') || '') + ' ';
        var pos = tags.indexOf(tag);
        if (pos == -1) {
            if (addTag) {
                tags = tags + tag;
            }
        }
        else {
            if (!addTag) {
                tags = tags.replace(tag, '');
            }
        }
        var newTags = jsex.trim(tags);

        $node.data('tags', newTags);
        newData.push(newTags);
        buildNodeTagIcons($node, true);
    }

    undoman.onAction({
        action: 'data',
        dataName: 'tags',
        nodes: nodes,
        oriData: oriData,
        newData: newData
    });

    host.NotifyChange();
}

function hasTag($node, tag) {
    tag = tag + ' ';
    var tags = ($node.data('tags') || '') + ' ';
    var pos = tags.indexOf(tag);
    return pos != -1;
}

function buildTableDecorations($table) {
    $table.find('tr.node').each(function (n, tr) {
        buildNodeDecorations(tr);
    });
}

function buildNodeDecorations(node) {
    var $node = $(node);
    var $markers;
    if ($node.attr('data-reminders')) {
        $markers || ($markers = $node.find('.markers'));
        $markers.prepend('<img src="image/alarm.png" class="alarm"/>');
    }
    if ($node.data('tags')) {
        buildNodeTagIcons(node);
    }
    var color = $node.data('color');
    if (color) {
        if (color.charAt(0) == '#') {
            $node.css('background-color', color);
        }
        else {
            $node.addClass('todo-' + color);
        }
    }
    var priority = $node.data('priority');
    if (priority) {
        $markers || ($markers = $node.find('.markers'));
        $markers.prepend('<span class="priority"><img src="image/priority/' + priority + '.png"/></span>');
    }
}

function updateNodeDecorations($node) {
    var $markers = $node.find('.markers');
    $markers.empty();

    if ($node.attr('data-reminders')) {
        $markers.prepend('<img src="image/alarm.png" class="alarm"/>');
    }
    if ($node.data('tags')) {
        buildNodeTagIcons($node);
    }
    for (var n = 0; n < g_colorNames.length; n++) {
        $node.removeClass('todo-' + g_colorNames[n]);
    }
    var color = $node.data('color');
    if (color) {
        if (color.charAt(0) == '#') {
            $node.css('background-color', color);
        }
        else {
            $node.css('background-color', '');
            $node.addClass('todo-' + color);
        }
    }
    else {
        $node.css('background-color', '');
    }
    var priority = $node.data('priority');
    if (priority) {
        $markers.prepend('<span class="priority"><img src="image/priority/' + priority + '.png"/></span>');
    }
}

function buildNodeTagIcons(tr, update) {
    var $tr = $(tr);
    var tags = $tr.data('tags').split(' ');
    var icons = [];
    var n;
    for (n = 0; n < tags.length; n++) {
        var tag = tags[n];
        if (!tag) {
            continue;
        }
        icons[g_tagOrders[tag]] = '<img src="image/tag/' + tag + '.png" class="tag "' + tag + '/>';
    }
    var $markers = $tr.find('.markers');
    if (update) {
        $markers.find('img.tag').remove();
    }
    for (n in icons) {
        $markers.append(icons[n]);
    }
}

function showTitleIfNeeded(editable) {
    var $editable = $(editable);
    var overflow = false;
    var magicPadding;
    if ($editable.attr('data-progress') !== undefined) {
        return;
    }
    if (editable.nodeName == 'TD') {
        magicPadding = -1;
        if (undecorated.measure($editable).width + magicPadding > $editable.width()) {
            overflow = true;
        }
    }
    else {
        var x = $editable.position().left;
        magicPadding = 5;
        if (x + undecorated.measure($editable).width + magicPadding > $editable.parents('td:first').width()) {
            overflow = true;
        }
    }
    if (overflow) {
        $editable.attr('title', $editable.text());
    }
}

var showTitleTimer;
function setupOverflowTooltips($table) {
    $table.on('mouseenter', '.editable', function () {
        var editable = this;
        clearTimeout(showTitleTimer);
        showTitleTimer = setTimeout(function () {
            showTitleIfNeeded(editable);
        }, 10);
    });
    $table.on('mouseout', '.editable', function () {
        $(this).attr('title', '');
        clearTimeout(showTitleTimer);
        showTitleTimer = null;
    });
}

function setupReminder($node) {
    var reminders = $node.attr('data-reminders');
    window.setupReminderCallback = function (newReminders) {
        if (newReminders != reminders) {

            newReminders = '' + newReminders;
            undoman.onAction({
                action: 'attr',
                attrName: 'data-reminders',
                nodes: [$node[0]],
                oriAttr: reminders,
                newAttr: newReminders
            });

            host.NotifyChange();
            if (!newReminders) {
                $node.removeAttr('data-reminders');
                $node.find('img.alarm').remove();
            }
            else {
                $node.attr('data-reminders', newReminders);
                if (!$node.find('img.alarm').length) {
                    var $alarmIco = $('<img src="image/alarm.png" class="alarm"/>');
                    $node.find('span.markers').prepend($alarmIco);
                }
            }
        }

    };
    host.setupReminder(reminders);
}

function deleteTodo($table) {

    var nodes = todolist.getSelectedSeparatedBranchNodes();
    var toActiveNode = todolist.getNodeToActiveAfterDeletion(nodes);

    //*
    var action = undoman.onAction({
        action: 'delete',
        nodes: nodes
    });

    todolist.deleteNode(nodes);
    todolist.notifyRowVisibilityChanged();
    if (toActiveNode) {
        todolist.setActiveRow(toActiveNode);
    }

    action.afterAction();
    gotFocus();

    //ttt
    $table.trigger('rowsChanged');

    host.NotifyChange();
    //*/
}

var pressingContextMenuKey;
function onTblTodoKeydown(e) {
//    return;
    if (undecorated.keydownIntercepted) {
        return;
    }
    var $table = $(this);

    var keyCode = e.which;
    pressingContextMenuKey = (keyCode == 93);

    var eat = false;
    var $node;
    if (keyCode == 45 || keyCode == 79) { // ins/o
        addTodo(host.isTopDownMode() ? !e.shiftKey : e.shiftKey, e.altKey);
        eat = true;
    }
    if (keyCode == 46) { // del
        deleteTodo($table);
    }
    $node = $(todolist.getActiveRow());
    var nodes;
    if (e.ctrlKey && $node.length) {
        if (keyCode == 65) { // a
            if (!$table.editable('isEditing')) {
                toggleNodeClass('active-todo', $table, $node);
                eat = true;
            }
        }
        else if (keyCode == 67) { //c
            nodes = todolist.getSelectedSeparatedBranchNodes();
            clip.copy(nodes);
            eat = true;
        }
        else if (keyCode == 88) { //x
            nodes = todolist.getSelectedSeparatedBranchNodes();
            clip.copy(nodes);
            deleteTodo($table);
            eat = true;
        }
        else if (keyCode == 86) { //v
            var node = todolist.getActiveRow();


            nodes = clip.paste(node, host.isTopDownMode());

            $table.trigger('rowsChanged');

            host.NotifyChange();
            eat = true;
        }
        else if (keyCode == 73) { // i
            toggleNodeClass('italic', $table, $node);
            eat = true;
        }
        else if (keyCode == 66) { // b
            toggleNodeClass('bold', $table, $node);
            eat = true;
        }
        else if (keyCode == 85) { // u
            toggleNodeClass('underline', $table, $node);
            eat = true;
        }
        //
        if (!g_hosted) {
            if (keyCode == 90 && e.ctrlKey) { //^z
                if (undoman.canUndo()) {
                    undoman.undo();
                }
            }
            else if (keyCode == 89 && e.ctrlKey) { //^y
                if (undoman.canRedo()) {
                    undoman.redo();
                }
            }
        }
    }
    if (keyCode == 72) {
        eat = true;
    }
//                console.log("@@@@@@@@@@@@@@@@@@ " + keyCode);
    if (eat) {
        e.preventDefault();
    }
}

function addTodo(below, asChild) {

    //
    if ($table.editable('isEditing')) {
        $table.editable('commit');
    }

    if (todolist.isEmpty()) {
        asChild = true;
        below = true;
    }


    var action = undoman.newAction({
        action: 'new'
    });

    var tclip = new Clipboard();
    tclip.setTable($table);
    var html = $('#node-template-container').html();
    var relNode = todolist.getActiveRow();
    var pastedNodes = tclip.pasteByRootNodesHtml(html, relNode, below, asChild);
    var $node = $(pastedNodes[0]);
    if (asChild) {
//        $table.treeTable('expandNode', relNode);
    }

    todolist.setActiveRow($node);
//    $table.trigger('setActiveRowRequest', $node);
    $node.find('span.label').trigger('editRequest');

    $node.attr('data-createTime', jsex.formatDate(new Date(), 'yyMMddhhmm'));

    action.nodes = pastedNodes;
    undoman.onAction(action).afterAction();

    //ttt
    $table.trigger('rowsChanged');

    host.NotifyChange();
}


////////////////////////////
function _getTodo($tr) {
    var $tds = $tr.children('td');
    var $td0 = $($tds[0]);
    var displayStyle = 'normal';
    if ($tr.hasClass('italic')) {
        displayStyle += ',italic';
    }
    if ($tr.hasClass('bold')) {
        displayStyle += ',bold';
    }
    if ($tr.hasClass('underline')) {
        displayStyle += ',underline';
    }
    var tags = $tr.data('tags') || '';
    var todo = interop.NewTodo();
    todo.id = $tr.data('id')||null;
    todo.Title = $td0.find('span.label').getEditText();
    todo.Progress = $($tds[1]).children().progressBar('value');
    todo.Due = $($tds[2]).getEditText();
    todo.Description = $($tds[3]).getEditText();

    var checked = $td0.find(':checkbox').is(':checked');
    todo.Checked = checked ? 1 : 0;

    todo.Active = $tr.hasClass('active-todo') ? 1 : 0;
    interop.SetTodoDisplayStyle(todo, displayStyle);
    interop.SetTodoCreateTime(todo, $tr.attr('data-createTime'));

    if(checked) {
        interop.SetTodoCheckTime(todo, $tr.attr('data-checkTime'));
    }

    var reminders = interop.NewReminders();
    var jsReminders = JSON.parse($tr.attr('data-reminders') || '[]');
    for (var n = 0; n < jsReminders.length; n++) {
        var jsReminder = jsReminders[n];
        var reminder = interop.NewReminder();
        interop.SetReminderType(reminder, jsReminder.Type);

        var timing = interop.NewTiming();
        interop.SetTimingType(timing, jsReminder.Timing.Type);
        timing.Value = jsReminder.Timing.Value;
        reminder.Timing = timing;

        reminders.Add(reminder);
    }
    interop.SetTodoReminders(todo, reminders);

    interop.SetTodoTags(todo, tags);
    todo.Color = $tr.data('color') || '';
    todo.Priority = $tr.data('priority') || 0;
    var children = $tr.data('children');
    if (children && children.length) {
        var subTodos = _getTodos(children);
        interop.SetTodoTodos(todo, subTodos);
    }
    return todo;
}

function _getTodos(trs) {
    var todos = interop.NewTodos();
    for (var n = 0; n < trs.length; n++) {
        var $tr = $(trs[n]);
        var todo = _getTodo($tr);
        todos.Add(todo);
    }
    return todos;
}


function getTodos(todosResult) {
//    var $rootTrs = $table.find('>tbody>tr[data-level="0"]');
    var nodes = $(todolist.getRootNode()).data('children');
    var todos = _getTodos(nodes);
    todosResult.Todos = todos;
}

function commitEdit() {
    $table.editable('commit');
}

function loadColWidths() {
    var $ths = $table.children('thead').find('th');
    var widths = host.getColWidths();
    for (var n = 0; n < widths.length; n++) {
        var $th = $($ths[n]);
        var width = widths[n];
        if (!width) {
            continue;
        }
        $th.dataAttr('width', width);
    }
    console.log('?');
}

function getColWidths() {
    var $ths = $table.find('thead th');
    var widths = [];
    var totalWidths = 0;
    var width;
    for (var n = 0; n < $ths.length; n++) {
        var $th = $($ths[n]);
        var visible = $th.is(':visible');
        if (!visible || parseInt($th.attr('data-width')) > 1) {
            widths.push(0);
        }
        else {
            width = $th.width();
            totalWidths += width;
            widths.push(width);
        }
    }
//    totalWidths = $table.width();
    for (n = 0; n < widths.length; n++) {
        width = widths[n];
        if (width) {
            widths[n] = width / totalWidths;
        }
    }
    var result = widths.join(',');
    return result;
}

function getCollapsedItems(resultWrapper) {
    var $trs = $table.find('>tbody>tr.collapsed');
    var infos = interop.NewCollapsedItemInfos();
    for (var n = 0; n < $trs.length; n++) {
        var $tr = $($trs[n]);
        var info = interop.NewCollapsedItemInfo();
        info.Row = $tr.index();
        info.Level = $tr.data('level');
        info.Title = $tr.find('span.label').getEditText();
        infos.Add(info);
    }
    resultWrapper.value = infos;
}

//
var gotFocusTimer;
function gotFocus() {
    console.log('~gotFocus');
//    return;
    clearTimeout(gotFocusTimer);
    gotFocusTimer = setTimeout(function () {
        gotFocusTimer = null;
        try {
            if ($table.editable('isEditing')) {
                var st = $(window).scrollTop();
                $table.editable('focus');
                $(window).scrollTop(st);
                return;
            }
            var tr;
            var el = document.activeElement;
            if (el && (el.nodeName == 'INPUT' || el.nodeName == 'TEXTAREA')) {
                return;
            }
            if ($(el).is('.editable')) {
//                tr = $(el).parents('tr:first')[0];
                var scrolltop = $(window).scrollTop();
                el.focus();
                $(window).scrollTop(scrolltop);
            }
            else {
                var scrolltop = $(window).scrollTop();
                tr = todolist.getActiveRow();
                if (tr && document.body.contains(tr)) {
                    $(tr).find('.editable:first').focus();
                }
                else {
                    $table[0].focus();
                }
                $(window).scrollTop(scrolltop);
            }
        }
        catch (e) {
            // quiet
        }
        console.log('gotFocus');
    }, 1);
}

function undo() {
    if (undoman.canUndo()) {
        undoman.undo();
    }
    else {
        console.log('cannot undo!');
    }
}

function redo() {
    if (undoman.canRedo()) {
        undoman.redo();
    }
    else {
        console.log('cannot redo!');
    }
}

function canUndo() {
    return undoman.canUndo();
}

function canRedo() {
    return undoman.canRedo();
}

function breakpoint() {
    throw new Error('breakpoint');
}

/////////
function toggleNodeClass(cssClass, $table, $node) {
    var newState = !$node.hasClass(cssClass);
    var nodes = todolist.getSelectedRows();
    var oriState = [];
    for (var n = 0; n < nodes.length; n++) {
        $node = $(nodes[n]);
        oriState.push($node.hasClass(cssClass));
        $node.toggleClass(cssClass, newState);
    }
    undoman.onAction({
        action: 'cssClass',
        nodes: nodes,
        cssClass: cssClass,
        oriState: oriState,
        newState: newState
    });
    host.NotifyChange();
}

function updateNodeData($table, $node, dataName, dataValue) {
    var nodes = todolist.getSelectedRows();

    var oriData = [];
    for (var n = 0; n < nodes.length; n++) {
        $node = $(nodes[n]);
        oriData.push($node.data(dataName));
        $node.data(dataName, dataValue);
        updateNodeDecorations($node);
    }

    undoman.onAction({
        action: 'data',
        dataName: dataName,
        nodes: nodes,
        oriData: oriData,
        newData: dataValue
    });

    host.NotifyChange();
}

function setInterop(pInterop) {
    interop = pInterop;
}

//
//
function setColumnVisible(colName, visible) {
    var className = 'hide' + colName.charAt(0).toUpperCase() + colName.substr(1);
    $table.toggleClass(className, !visible);
    $table.children('thead').find('th[data-' + colName + ']').toggleClass('hidden', !visible);

//    $tblTodo.css('table-layout', '');

    $table.colResizer('updateVisibleThs');
    fitTableWidth(true);
}

function setDoneItemsVisible(visible) {
    $table.toggleClass('hideChecked', !visible);
}

function setTheme(themeName) {
    var $body = $(document.body);
    $body.removeClass('green-theme chinese-theme');
    if (themeName && themeName != 'default') {
        $body.addClass(themeName + '-theme');
    }
    var oldShowAltRow = window.showAltRow || false;
    window.showAltRow = themeName === 'green';
    if (oldShowAltRow != window.showAltRow && !oldShowAltRow) {
//        $('#tblTodo').treeTable('updateAltRows');
    }
    fitTableWidth(true);
}

function flashTodo(node, count) {
    var $node = $(node);
    if (!count) {
        count = 0;
    }
    $node.removeClass('flashing0 flashing1');
    if (count < 5) {
        $node.addClass('flashing' + count % 2);
        setTimeout(function () {
            flashTodo(node, count + 1);
        }, 150);
    }
}

function localize($table) {
    var map = host.getLocalizedStrings();
    if (!map) {
        return;
    }
    var $ths = $table.children('thead').find('th');
    $($ths[0]).children().text(map['header.title']);
    $($ths[1]).children().text(map['header.progress']);
    $($ths[2]).children().text(map['header.due']);
    $($ths[3]).children().text(map['header.description']);
    var $emptyIndicator = $table.children('tfoot.empty-indicator');
    $emptyIndicator.find('span').text(map['msg.todolist.empty']);
    $emptyIndicator.find('a').text(map['action.add']);


    var $ulMenu1 = $('#ulMenu1');

    function setMenuText(name, key) {
        $ulMenu1.children('[data-name=' + name + ']').children('label').text(map[key]);
    }

    setMenuText('mnActive', 'menu.active');
    setMenuText('mnTag', 'menu.tag');
    setMenuText('mnColor', 'menu.color');
    setMenuText('mnPriority', 'menu.priority');
    setMenuText('mnSetupReminder', 'menu.setReminder');

    g_tagLabels = map['tagLabels'].split(',');
    g_colorLabels = map['colorLabels'].split(',');

    T['None'] = map['lbl.none'];

    //
    $('input.btnOk').val(map['action.ok']);
    $('input.btnToday').val(map['action.today']);
    $('input.btnClear').val(map['action.clear']);

    //
    $.fn.datetimePicker.setMonthAbbrs(map['monthAbbrs'].split(','));
    var $weekDayThs = $('table.calendar th');
    var weekDayAbbrs = map['weekDayAbbrs'].split(',');
    for (var n = 0; n < $weekDayThs.length; n++) {
        $($weekDayThs[n]).text(weekDayAbbrs[n]).addClass('zh');
    }

}

function T(key) {
    return T[key] || key;
}

function window_beforePrint() {
    //*
    var $imgs = $('img[src="image/line-udr.gif"]');
    $imgs.attr('src', 'image/print/line-udr.gif');
    $imgs = $('img[src="image/line-ud.gif"]');
    $imgs.attr('src', 'image/print/line-ud.gif');
    $imgs = $('img[src="image/line-ur.gif"]');
    $imgs.attr('src', 'image/print/line-ur.gif');
    //*/
    //?todolist.updateAltRows(); hidden rows
}

function window_afterPrint() {
    //?
}


/**********************************************************************************/

var lastClientWidth;
function fitTableWidth(updateCols) {

//    var thWidth1 = $th1.width();
//    var tableWidth = $table.width();

//    var clientWidth = document.body.clientWidth;
    var fullWidth = divFullWidth.clientWidth;//domex.getCurrentStyle(divFullWidth, 'width');

    //test

//    var winWidth = window.innerWidth;
//    console.log('winWidth: ' + winWidth + ', clientWidth: ' + clientWidth + ', fullWidth: ' + fullWidth);

        var clientWidth = fullWidth;

    //
    if (clientWidth < 1) {
        return;
    }
    if (!updateCols && lastClientWidth == clientWidth) {
        return;
    }

    var tableWidth0 = $table.width();
    var headerVisible = !$table.hasClass('hideHeader');
    if(headerVisible) {
        var $ths = $table.children('thead').find('th');
        var widths = [];
        for (var n = 0; n < $ths.length; n++) {
            widths.push($($ths[n]).width());// / tableWidth0);
        }
        $ths.width('');
    }

    $table.css('width', '100%');
    $table.width($table.width());
    $table.width(clientWidth);

    var tableWidth1 = $table.width();
    if (Math.abs(tableWidth1 - tableWidth0) > 1 || updateCols) {
        $table.colResizer('setup');
    }
    else {
        if(headerVisible) {
            for (n = $ths.length - 1; n > 0; --n) {
                $($ths[n]).width(widths[n]);
            }
        }
    }
    lastClientWidth = clientWidth;
}

var resizing = false;
var lastWindowWidth;
$(window).resize(function () {
    if (resizing) {
        return;
    }
    var width = document.body.clientWidth;
    if (!width || lastWindowWidth == width) {
        return;
    }
    lastWindowWidth = width;
    console.log('window resize: ' + width);

    resizing = true;
    fitTableWidth();

    $table.triggerHandler('resize');
    resizing = false;
});